﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Mvc;

using BLL.Configuration;
using BLL.Extensions;

using Yahoo.Yui.Compressor;

namespace Web.Infrastructure
{
    public static class Bundles
    {
        private static readonly Dictionary<string, MvcHtmlString> _cssResult =
            new Dictionary<string, MvcHtmlString>();

        private static readonly Dictionary<string, MvcHtmlString> _jsResult =
            new Dictionary<string, MvcHtmlString>();

        private static readonly object _suncCssObject = new object();

        private static readonly object _suncJsObject = new object();

        private static IGlobalSettings GlobalSettings
        {
            get
            {
                return DependencyResolver.Current.GetService<IGlobalSettings>();
            }
        }

        /// <summary>
        /// Writes the CSS.
        /// </summary>
        /// <param name="helper">The helper.</param>
        /// <param name="compressionUrl">The compression URL.</param>
        /// <param name="cssUrls">The CSS urls.</param>
        public static MvcHtmlString WriteCss(
            this UrlHelper helper, string compressionUrl, params string[] cssUrls)
        {
            if (!_cssResult.ContainsKey(compressionUrl))
            {
                if (GlobalSettings.Compress.IsEnabledForCss)
                {
                    var compressor = new CssCompressor();
                    compressor.RemoveComments = true;
                    var compressedCss = new StringBuilder();
                    foreach (var css in cssUrls)
                        compressedCss.Append(
                            compressor.Compress(ReadText(HttpContext.Current.Server.MapPath(css))));
                    if (GlobalSettings.Compress.IsEnabledForImage)
                        compressedCss = ConvertImages(compressedCss, GetFolder(compressionUrl));
                    lock (_suncCssObject)
                    {
                        using (
                            var writer =
                                (TextWriter)
                                new StreamWriter(HttpContext.Current.Server.MapPath(compressionUrl), false))
                        {
                            writer.Write(compressedCss.ToString());
                            writer.Close();
                        }
                    }

                    _cssResult.Add(compressionUrl,
                                MvcHtmlString.Create(string.Format(
                                "<link href=\"{0}?{1}\" rel=\"stylesheet\">",
                                helper.Content(compressionUrl),
                                Assembly.GetExecutingAssembly().GetName().Version)));
                    return _cssResult[compressionUrl];
                }
                var result = new StringBuilder();
                var version = Assembly.GetExecutingAssembly().GetName().Version;
                foreach (var url in cssUrls)
                    result.Append(
                        "<link href=\"" + helper.Content(url) + "?" + version + "\" rel=\"stylesheet\">");
                _cssResult.Add(compressionUrl, MvcHtmlString.Create(result.ToString()));
                return _cssResult[compressionUrl];
            }
            return _cssResult[compressionUrl];
        }

        /// <summary>
        /// Writes the js.
        /// </summary>
        /// <param name="helper">The helper.</param>
        /// <param name="compressionUrl">The compression URL.</param>
        /// <param name="jsUrls">The js urls.</param>
        public static MvcHtmlString WriteJs(this UrlHelper helper, string compressionUrl, params string[] jsUrls)
        {
            if (!_jsResult.ContainsKey(compressionUrl))
            {
                if (GlobalSettings.Compress.IsEnabledForJs)
                {
                    var compressor = new JavaScriptCompressor();
                    var fullJs = new StringBuilder();
                    foreach (var js in jsUrls)
                        fullJs.Append(ReadText(HttpContext.Current.Server.MapPath(js)));
                    lock (_suncJsObject)
                    {
                        using (
                            var writer =
                                (TextWriter)
                                new StreamWriter(HttpContext.Current.Server.MapPath(compressionUrl), false))
                        {
                            writer.Write(compressor.Compress(fullJs.ToString()));
                            writer.Close();
                        }
                    }
                    _jsResult.Add(compressionUrl,
                                MvcHtmlString.Create(string.Format("<script type=\"text/javascript\" src=\"{0}?{1}\"></script>",
                                helper.Content(compressionUrl),
                                Assembly.GetExecutingAssembly().GetName().Version)));
                    return _jsResult[compressionUrl];
                }
                var result = new StringBuilder();
                var version = Assembly.GetExecutingAssembly().GetName().Version;
                foreach (var url in jsUrls)
                {
                    result.Append(
                        "<script type=\"text/javascript\" src=\"" + helper.Content(url) + "?" + version
                        + "\"></script>");
                }
                _jsResult.Add(compressionUrl, MvcHtmlString.Create(result.ToString()));
                return _jsResult[compressionUrl];
            }
            return _jsResult[compressionUrl];
        }

        /// <summary>
        /// Reads the text.
        /// </summary>
        /// <param name="path">The path.</param>
        private static string ReadText(string path)
        {
            using (var reader = (TextReader)new StreamReader(path))
            {
                return reader.ReadToEnd();
            }
        }

        /// <summary>
        /// Gets the folder.
        /// </summary>
        /// <param name="path">The path.</param>
        private static string GetFolder(string path)
        {
            var file = new FileInfo(HttpContext.Current.Server.MapPath(path));
            if (file.Directory != null)
                return file.Directory.FullName;

            return null;
        }

        /// <summary>
        /// Converts the images.
        /// </summary>
        /// <param name="css">The CSS.</param>
        /// <param name="cssPath">The CSS path.</param>
        private static StringBuilder ConvertImages(StringBuilder css, string cssPath)
        {
            var background = new Regex(@"url\((.*?)\)");
            var stringCss = css.ToString();
            foreach (Match backgroundUrl in background.Matches(stringCss))
            {
                var url = backgroundUrl.Groups[1];
                var newUrl = GetNewUrl(url.Value, cssPath);
                if (url.Value != newUrl)
                    css.Replace(url.Value, newUrl);
            }
            return css;
        }

        private static string GetNewUrl(string url, string path)
        {
            try
            {
                var newUrl = Path.Combine(path, url.Replace("'", string.Empty).Replace(@"""", string.Empty));
                var file = File.Open(newUrl, FileMode.Open);
                var bytes = file.ReadToEnd();
                if (bytes.Length < (1024 * 32))
                {
                    var imageAsString = Convert.ToBase64String(bytes);
                    var extension = Path.GetExtension(newUrl);
                    file.Close();
                    return string.Format("data:image/{0};base64,{1}", extension, imageAsString);
                }
                file.Close();
                return url;
            }
            catch (FileNotFoundException)
            {
            }
            return url;
        }
    }
}